/**
 * 参考链接
 * 官网rect使用教程  @link https://konvajs.org/docs/shapes/Rect.html
 * 官网rect.size()api  @link  https://konvajs.org/api/Konva.Rect.html#size__anchor
 * 官网 Transformer api @link http://konvajs-doc.bluehymn.com/docs/select_and_transform/Keep_Ratio.html
 * 
 * 官网 Konva.Group api @link https://konvajs.org/api/Konva.Group.html
 * 官网缩放显示图片案例 @link https://konvajs.org/docs/sandbox/Scale_Image_To_Fit.html
 * 
 * 等比缩放图片 @link http://konvajs-doc.bluehymn.com/docs/select_and_transform/Keep_Ratio.html
 * 
 * 
 * 
 * 问题
 * @done #3788 node.on(),只有两个参数，没法取消事件捕获；可以给layer2建一个透明的节点，撑满它。实现超区域结束move事件
 *          判断超区还要小心碰到正在绘制的Rect，判断条件改好。这里如果用事件冒泡不合适，因为我们监听的是layer2
 *          明确给layer2 id了，evt.target 还是没用，或许应该用一个group,这样优雅点；
 *          
 * @done node.toImage 生成的图片模糊
 *      导入截图缩放了，忘记放大了，真实使用场景应该不需要缩放截图
 * @todo konva 中 evt.currentTarget 和 evt.target什么区别
 * @done opencv 脚本太大了，延时加载脚本 。defer和async的区别；
 *          博文  @link https://blog.csdn.net/VickyTsai/article/details/102841293
 * 
 * 代办（优先）
 * @todo 返回上一步时，局部变量没删除,可能会引发内存泄漏
 * @todo 操作提示文字功能完善
 * @todo 应用移动端、pc端适配
 * @todo 缩放模版匹配识别符石结果不准确，符石模板图片太小太模糊了
 * @todo 小分辨率屏上，符石模版图片可能比实际大
 * @todo 缩放模版匹配误差还是太大了
 *          增加流程->批量选区确认->直接比认符石（优先）
 *          需要能识别出是否为符石（优先）
 * @todo 检查绘制选区是否有超原图尺寸的可能
 * 
 * 
 * 
 * 优化
 * @todo 绘制指针阈值好像受不同分辨率影响
 * @done 缩放、移动后，获取rect在原图上的实际值
 * @todo lib/opencv@4.5.5.min.js 8MB,太大了，可以应用中下载 或者只抽离使用的方法
 * @todo css类名规范化，理解 - _ 大小写的使用区别
 *              
 * 
 */
/**
 * @description 操作步骤
 * @constant 
 * @readonly
 * @enum {number}
 */
const OP_STEP = {
    /** 初始状态 */
    A: 0,
    /** 绘制背包区域完成 */
    B: 1,
    /** 绘制符石案例区域 */
    C: 2,
};


const IS_DEBUG = true;// 是否开始调试，方便调样式
const IS_DEBUG_DEF_RUNE = true;// 是否开始符石识别调试，方便调样式
// const DEBUG_CURR_STEP = OP_STEP.B;
const DEBUG_CURR_STEP = OP_STEP.C;
/**@constant */
const DEF_NUMS_AREA = [1, 4];// 默认识别数量范围（考虑背包中单个rune数量达到5的情况很少很少，可选值1~7，数字0,8模板要特殊处理暂不考虑）

/**
 * @typedef {Object} Rune
 * @property {string} src   rune图片存储位置
 * @property {string} name  rune名称
 * @property {number} order rune序号
 */



/**
 * @typedef {Object} Num
 * @property {string} src   数字图片存储位置
 * @property {string} name   数字值字符串
 * @property {number} order 数字序号
 */
/**
 * 
 * @typedef {Object} Rect
 * @property {number} x
 * @property {number} y
 * @property {number} width
 * @property {number} height
 */

/**
 * @typedef {Object} Loc
 * @typedef {number} x
 * @typedef {number} y
 */


const vw = window.innerWidth;
const vh = window.innerHeight;

// @todo type
let imgData = {
    img: null,
    size: {
        width: 0,
        height: 0,
        origin_width: 0,
        origin_height: 0,
    },
    scale: 1,
};

const stage = new Konva.Stage({
    container: 'container',
    width: window.innerWidth,
    height: window.innerHeight
});
const layer1 = new Konva.Layer({
    id: "layer1",
    name: "layer1",
});
const layer2 = new Konva.Layer({
    id: "layer2",
    name: "layer2",
});

const draw_pint_threshold = 25;// 模版图片尺寸 22 x 24，待匹配图片不能小于模板图片

let is_drawing_bag_area = false;
let is_drawing_rune_area = false;

const outer1_id = "outer1";
const image1_id = "image1";
const image1_o_id = "image1_o";

const outer2_id = "outer2"; // #3788
const image2_id = "image2"; // #3788



let curr_step = OP_STEP.A;

// const rect_stroke_color = "red";
const rect_stroke_color1 = "rgba(0,255,255,255)";
const rect_stroke_color2 = "rgba(255,0,125,255)";
let text = new Konva.Text({
    x: 10,
    y: 10,
    fontFamily: 'Calibri',
    fontSize: 24,
    text: '我是文本',
    // fill: 'white',
    fill: 'red',
});
let text2 = new Konva.Text({
    x: 10,
    y: 10,
    fontFamily: 'Calibri',
    fontSize: 24,
    text: '我是文本',
    // fill: 'white',
    fill: 'red',
});


layer1.add(text);

let drawing_rect1 = false;
let drawing_rect2 = false;
let rect1 = null;
let rect2 = null;

let bagRect = null;
let runeRect = null;
let start_point1 = null;
let start_point2 = null;

let image2_c = null; // 备份的image2,调试显示用
let image3 = null;
// Konva.Vector2d

/**
 * @constant
 * @type {Rune[]}
 */
const RUNES_DATA = [{
    src: "/runes/rune_1_SIL.png",
    name: "SIL",
    order: 1
},
{
    src: "/runes/rune_2_ZER.png",
    name: "ZER",
    order: 2
},
{
    src: "/runes/rune_3_MON.png",
    name: "MON",
    order: 3
},
{
    src: "/runes/rune_4_KRA.png",
    name: "KRA",
    order: 4
},
{
    src: "/runes/rune_5_OGI.png",
    name: "OGI",
    order: 5
},
{
    src: "/runes/rune_6_DOL.png",
    name: "DOL",
    order: 6
},
{
    src: "/runes/rune_7_TRU.png",
    name: "TRU",
    order: 7
},
{
    src: "/runes/rune_8_CIM.png",
    name: "CIM",
    order: 8
},
{
    src: "/runes/rune_9_SIV.png",
    name: "SIV",
    order: 9
},
{
    src: "/runes/rune_10_KLY.png",
    name: "KLY",
    order: 10
},
{
    src: "/runes/rune_11_ZOB.png",
    name: "ZOB",
    order: 11
},
{
    src: "/runes/rune_12_BOR.png",
    name: "BOR",
    order: 12
},
{
    src: "/runes/rune_13_ODO.png",
    name: "ODO",
    order: 13
},
{
    src: "/runes/rune_14_ZOL.png",
    name: "ZOL",
    order: 14
},
{
    src: "/runes/rune_15_PYD.png",
    name: "PYD",
    order: 15
},
{
    src: "/runes/rune_16_SAK.png",
    name: "SAK",
    order: 16
},
{
    src: "/runes/rune_17_VIN.png",
    name: "VIN",
    order: 17
},
{
    src: "/runes/rune_18_DOS.png",
    name: "DOS",
    order: 18
},
{
    src: "/runes/rune_19_COL.png",
    name: "COL",
    order: 19
},
{
    src: "/runes/rune_20_COS.png",
    name: "COS",
    order: 20
}
];

/**
     * @constant
     * @type {Num[]}
     */
const NUMS_DATA = [{
    src: "/nums/num_0.png",
    name: "0",
    order: 0
}, {
    src: "/nums/num_1.png",
    name: "1",
    order: 1
}, {
    src: "/nums/num_2.png",
    name: "2",
    order: 2
}, {
    src: "/nums/num_3.png",
    name: "3",
    order: 3
}, {
    src: "/nums/num_4.png",
    name: "4",
    order: 4
}, {
    src: "/nums/num_5.png",
    name: "5",
    order: 5
}, {
    src: "/nums/num_6.png",
    name: "6",
    order: 6
}, {
    src: "/nums/num_7.png",
    name: "7",
    order: 7
}, {
    src: "/nums/num_8.png",
    name: "8",
    order: 8
}, {
    src: "/nums/num_9.png",
    name: "9",
    order: 9
}]

/**
 * @type { Map<string, HTMLImageElement> }
 * ！！！注意保证 rune.name 唯一
 * key: rune_name value:img 加载的图片
 */
let rune_imgs = new Map(); // 存放加载的runes图片
/**
 * @type { Map<string, HTMLImageElement> }
 */
let num_imgs = new Map(); // 存放加载的数字图片


/**
 * @function 
 * @description 调试用，快速切换到指定步骤;需要配置下bagRect或runeRect，可以按正常运行;也不是不能封装复用btn_ok 中的处理函数，但这样写放一块也还行吧 配合 IS_DEBUG DEBUG_CURR_STEP使用
 * @param { OP_STEP }  currStep
 */
async function debugStep(currStep) {
    curr_step = OP_STEP.B;
    o_btn_restart.style.display = "none";
    o_btn_quit.style.display = "none";
    o_btn_ok.style.display = "none";

    o_btn_prev_step.style.display = "block";
    o_btn_start1.style.display = "none";
    o_btn_start2.style.display = "block";

    // bagRect = { x: 278, y: 1479, width: 836, height: 682 };// 模拟数据
    bagRect = { x: 245, y: 1462, width: 877, height: 391 };// 模拟数据

    // let img1 = layer1.findOne('#img1');
    // let img1 = layer1.findOne("#" + image1_id);
    let img1_o = layer1.findOne("#" + image1_o_id);
    // console.log(img1);
    let img2 = img1_o.clone().opacity(1);
    await new Promise((res, rej) => {
        img2.toImage({
            callback(img) {
                res(img);
            },
            mimeType: "image/jpeg",
            x: bagRect.x,
            y: bagRect.y,
            width: bagRect.width,
            height: bagRect.height,
            quality: 1, // 0-1
            pixelRatio: 1, // 
            imageSmoothingEnabled: true,
        });
    }).then(img => {
        // console.log("xxx", img1);// htmlEl
        let sc_w = img.width;
        let sc_h = img.height;

        let image = new Konva.Image({
            id: image2_id,
            image: img,
            x: vw * 0.1,
            y: vh * 0.2,
            width: sc_w,
            height: sc_h,
            name: 'image2',
            draggable: true, // 拖拽是图片节点的功能
            // opacity: 0.1
        });
        layer2.add(image);
        image2_c = image.clone();
        image2_c.id("image2_c");

        let tr = new Konva.Transformer({
            node: image,
            id: "tr_bag", // 无效
            keepRatio: true,
            // flipEnabled 好像可以裁剪
            rotateEnabled: false, // 禁止旋转
            enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right']
        });
        // console.log(tr);
        layer2.add(tr);
        // layer.draw();
    });

    console.log(image2_c);

    layer1.hide();
    layer2.show();


    if (currStep == OP_STEP.C) {
        curr_step = OP_STEP.C;

        o_btn_start2.style.display = "none";
        o_btn_prev_step.style.display = "none";

        o_btn_restart.style.display = "none";
        o_btn_quit.style.display = "none";
        o_btn_ok.style.display = "none";

        o_btn_back.style.display = "block";
        // o_btn_finish.style.display = "block";

        let tr = layer2.findOne('#tr_bag');
        tr.resizeEnabled(false);

        let bag_area_img = layer2.findOne("#" + image2_id);
        bag_area_img.draggable(false);

        // 根据选区生成图片
        // runeRect = { x: 171, y: 187, width: 156, height: 153 };// 模拟数据 kly
        runeRect = { x: 553, y: 211, width: 125, height: 143 };// 模拟数据 cos
        // runeRect = { x: 382, y: 217, width: 132, height: 137 };// 模拟数据 sak
        console.log(runeRect);
        // return;

        let image2_ = image2_c.clone();
        image2_.position({
            x: 0,
            y: 0
        });


        image2_.toImage({
            callback(img) {

                // console.log(img);
                document.getElementById("check_menu").style.display = "block";
                document.getElementById("td-rune_rect").appendChild(img);

                let sc_w = img.width;
                let sc_h = img.height;
                image3 = new Konva.Image({
                    id: image2_id,
                    image: img,
                    x: 0,
                    y: 0,
                    width: sc_w,
                    height: sc_h,
                    name: 'image3',
                    draggable: true, // 拖拽是图片节点的功能
                    // opacity: 0.1
                });

            },
            mimeType: "image/jpeg",
            x: runeRect.x,
            y: runeRect.y,
            width: runeRect.width,
            height: runeRect.height,
            quality: 1, // 0-1
            pixelRatio: 1, // 
            imageSmoothingEnabled: true,
        });
    }
}

showScImg();

let sel_innerhtml = "";
RUNES_DATA.forEach((el, index) => {
    sel_innerhtml += `<option value="${el.name}" ${index == 0 ? 'selected' : ''}>${el.name}</option>`;
});

document.getElementById("select_rune").innerHTML = sel_innerhtml;


/**
 * @todo 这里好像不需要 async await
 * @todo 这个图片导入两次了
 * imgData.img base64 (new Image()).src = base64
 */
Promise.all([loadImg(imgData.img).then(img => {
    /**
    * @done 根据图片宽高调整canvas,不同的手机截图尺寸可能不一样
    * @todo 根据匹配结果设置canvas尺寸
    * @todo 当前数据识别进度展示
    * @todo 停止数据识别与恢复数据识别与重新数据识别 js线程 https://docs.opencv.org/4.5.5/d9/df5/tutorial_js_intelligent_scissors.html
    */
    let canvas = document.getElementById('imageCanvasInput');
    let ctx = canvas.getContext('2d', {
        willReadFrequently: true,
    });
    // console.log("导入的截图信息:", img.width, img.height);
    // console.log("canvas信息:", canvas.width, canvas.height);
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);


    let canvas_out = document.getElementById('screenshot_bag_marked_Canvas');
    let ctx_out = canvas_out.getContext('2d', {
        willReadFrequently: true,
    });
    canvas_out.width = img.width;
    canvas_out.height = img.height;
    ctx_out.drawImage(img, 0, 0);
}), ...RUNES_DATA.map(async (el) => {
    let src = "../../imgs" + el.src;
    let name = el.name;
    await loadImg(src).then(img => {
        // console.log(img);
        rune_imgs.set(name, img);
    });
}), ...NUMS_DATA.map(async (el) => {
    let src = "../../imgs" + el.src;
    let name = el.name;
    return (
        await loadImg(src).then(img => {
            num_imgs.set(name, img);
        })
    );
})]).then(() => {
    // console.log("图片资源加载完成", rune_imgs);
    if (IS_DEBUG_DEF_RUNE)
        setTimeout(() => {
            defRune();
        }, 1000);

});

function defRune() {
    // console.log(cv);
    // console.log(window.$mocvf);
    console.time("识别时间");

    /**@todo 局部变量该删除的删除，防止内存泄漏 */

    // -----获取背包区域
    let src_screenshot = cv.imread('imageCanvasInput'); // 待查询
    let src_s_c = src_screenshot.clone();// clone一份
    /**
     * @type {MatchedData} 
     */
    let bag_matched_data = {};
    {
        // 添加块级作用域，防止变量污染

        let dst = new cv.Mat();
        let mask = new cv.Mat();
        // console.log("bagRect:", bagRect);

        let match_rect = new cv.Rect(bagRect.x, bagRect.y, bagRect.width, bagRect.height);
        let matchLoc = { x: bagRect.x, y: bagRect.y };
        let point = new cv.Point(bagRect.x + bagRect.width, bagRect.y + bagRect.height);

        // -----绘制矩形选框
        let color_1 = new cv.Scalar(0, 255, 0, 255);// 背包框颜色
        cv.rectangle(src_s_c, matchLoc, point, color_1, 2, cv.LINE_8, 0);
        cv.imshow('screenshot_bag_marked_Canvas', src_s_c);
        cv.imshow("rune_rect_marked_Canvas", src_screenshot.roi(match_rect));

        document.getElementById('match_result_show').style.display = "block";
        document.getElementById('screenshot_bag_marked_Canvas').style.display = "block";
        document.getElementById('rune_rect_marked_Canvas').style.display = "block";


        dst.delete();
        mask.delete();
    }

    // -----获取符石区域
    {
        // 添加块级作用域，防止变量污染
        let src_bag = cv.imread('rune_rect_marked_Canvas'); // 背包选区，待查询
        let src_bag_c = src_bag.clone();

        let dst = new cv.Mat();
        let mask = new cv.Mat();
        // console.log("runeRect:", runeRect);

        let match_rect = new cv.Rect(runeRect.x, runeRect.y, runeRect.width, runeRect.height);
        let matchLoc = { x: runeRect.x, y: runeRect.y };
        let point = new cv.Point(runeRect.x + runeRect.width, runeRect.y + runeRect.height);

        // -----绘制矩形选框
        let color_2 = new cv.Scalar(255, 0, 0, 255);// 符石框颜色
        cv.rectangle(src_bag, matchLoc, point, color_2, 2, cv.LINE_8, 0);
        // cv.imshow('rune_rect_marked_Canvas', src_bag);
        cv.imshow("rune_rect_marked_Canvas", src_bag_c.roi(match_rect));

        // document.getElementById('match_result_show').style.display = "block";
        // document.getElementById('screenshot_bag_marked_Canvas').style.display = "block";
        // document.getElementById('rune_rect_marked_Canvas').style.display = "block";
        // document.getElementById('rune_rect_marked_Canvas').style.display = "block";


        dst.delete();
        mask.delete();
    }

    // 轮廓识别，选取大些的轮廓等待用户选择
    {
        let rune_area = cv.imread("rune_rect_marked_Canvas");
        let rune_area_c = rune_area.clone();
        cv.imshow("rune_contour_rects", rune_area_c);

        let arr_l = [100, 120, 100, 255];
        let arr_h = [200, 200, 200, 255];
        let img2_b_w = rune_area_c.clone();
        let low_hsv_src = new cv.Mat(img2_b_w.rows, img2_b_w.cols, img2_b_w.type(), arr_l);
        let high_hsv_src = new cv.Mat(img2_b_w.rows, img2_b_w.cols, img2_b_w.type(), arr_h);
        cv.inRange(img2_b_w, low_hsv_src, high_hsv_src, img2_b_w); // cv.CV_8U
        cv.imshow("rune_contour_rects", img2_b_w);

        let img1 = img2_b_w.clone();
        let contours = new cv.MatVector();
        let hierarchy = new cv.Mat();

        let img2 = cv.Mat.zeros(img1.rows, img1.cols, cv.CV_8UC3);
        try {
            cv.findContours(img1, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE);
        } catch (error) {
            console.error("轮廓查询异常:", error);
            return;
        }

        let count = 0;
        // console.log("总轮廓数量:", contours.size());
        /**@type {[Size]} */
        let rects = [];
        for (let i = 0; i < contours.size(); ++i) {

            let hier = hierarchy.intPtr(0, i);
            let cnt = contours.get(i);
            // let order_txt_color = new cv.Scalar(Math.round(Math.random() * 255), Math.round(Math .random() * 255), Math.round(Math.random() * 255));
            // cv.drawContours(img2, contours, i, order_txt_color, 1, cv.LINE_8, hierarchy, 100);


            // 外接矩形绘制
            let rect = cv.boundingRect(cnt);
            if (rect.width < 20 || rect.height < 20) {
                continue;
            }
            count++;
            rects.push(rect);

            /*
            let point1 = new cv.Point(rect.x, rect.y);
            let point2 = new cv.Point(rect.x + rect.width, rect.y + rect.height);

            let point = new cv.Point(rect.x + 5, rect.y - 5);
            cv.putText(img2, count + "", point, cv.FONT_HERSHEY_SIMPLEX, 0.4, order_txt_color, 1, cv.LINE_4); // 标序号

            cv.rectangle(img2, point1, point2, order_txt_color, 1, cv.LINE_AA, 0); // 绘制外接矩形
            */
            cnt.delete();
        }

        cv.imshow('rune_contour_rects', img2);


        // -----在konva canvas中展示 待选中选框
        const stage_rects = new Konva.Stage({
            container: 'pick_rect',
            width: 300,
            height: 300
        });

        const layer1 = new Konva.Layer({
            id: "layer1",
            name: "layer1",
        });
        let rune_img = image3.clone();
        const group1 = new Konva.Group({
            id: "group1",
            name: "group1",
            width: rune_img.width(),
            height: rune_img.height(),
            x: (stage_rects.width() - rune_img.width()) / 2,
            y: (stage_rects.height() - rune_img.height()) / 2,
        });

        // console.log(group1.width(), group1.height());
        console.log(stage_rects.width(), stage_rects.height());

        layer1.add(group1);
        stage_rects.add(layer1);

        let bg = new Konva.Rect({
            x: 0,
            y: 0,
            fill: 'yellow',
            width: layer1.width(),
            height: layer1.height()
        });
        layer1.add(bg);
        bg.moveToBottom();

        group1.add(rune_img);
        rune_img.draggable(false);
        rune_img.position({
            x: 0,
            y: 0
        });

        for (let i = 0; i < rects.length; i++) {
            let item = rects[i];
            let rect = new Konva.Rect({
                x: item.x,
                y: item.y,
                // fill: 'green',
                stroke: 'red',
                strokeWidth: 2,
                width: item.width,
                height: item.height
            });

            group1.add(rect);
        }









        img1.delete();
        img2.delete();

        contours.delete();
        hierarchy.delete();
    }

    return;

    // -----缩放模板匹配rune
    /**
     * @type {MatchedData} 
     */
    let runes_matched_data = {};
    {
        // 添加块级作用域，防止变量污染
        let canvas_rune_templ = document.getElementById('templateCanvasInput');
        let ctx_c_r_t = canvas_rune_templ.getContext('2d', {
            willReadFrequently: true,
        });

        // let src_bag = cv.imread('bag_rune_marked_Canvas'); // 背包选区，待查询
        let src_bag = cv.imread('rune_rect_marked_Canvas'); // 符石选区，待查询
        let src_bag_c = src_bag.clone();
        let rune_name = document.getElementById("select_rune").value;
        if (IS_DEBUG_DEF_RUNE)
            // rune_name = "KLY";
            rune_name = "COS";
        // rune_name = "SAK";
        // console.log(rune_name);
        let img_rune = rune_imgs.get(rune_name);
        if (img_rune === undefined) {
            console.warn(`rune ${rune_name} 图片加载失败`);
            return;
        }

        ctx_c_r_t.clearRect(0, 0, canvas_rune_templ.width, canvas_rune_templ.height); // 清空画布
        ctx_c_r_t.drawImage(img_rune, 0, 0);
        let templ = cv.imread('templateCanvasInput'); // Rune模板

        let dst = new cv.Mat();
        let mask = new cv.Mat();

        // -------二值化待搜索图片与模板图片，提高缩放模板查询精准度
        let arr_l = [140, 140, 140, 255];
        let arr_h = [200, 200, 200, 255];

        // 二值化待搜索图片
        let src_b_w = new cv.Mat();// 用于保存二值化图片
        bwImage(src_bag_c, arr_l, arr_h, src_b_w);

        // 转化数据 cv.CV_8U->cv.CV_8UC4
        cv.imshow("rune_rect_marked_Canvas", src_b_w);
        src_b_w = cv.imread("rune_rect_marked_Canvas"); // 二值化图
        cv.imshow("rune_rect_marked_Canvas", src_bag_c); // 最后显示的还是正常的图片

        // 二值化模板图片
        let templ_b_w = new cv.Mat();// 用于保存二值化图片
        bwImage(templ, arr_l, arr_h, templ_b_w);

        // 转化数据 cv.CV_8U->cv.CV_8UC4
        cv.imshow("templateCanvasInput", templ_b_w);
        templ_b_w = cv.imread("templateCanvasInput"); // 二值化图
        cv.imshow("templateCanvasInput", templ);

        // let ttt = templ_b_w.clone();
        // cv.resize(ttt, ttt, new cv.Size(103.3699, 112.7672, 0, 0, cv.INTER_CUBIC));
        // console.log(ttt.cols, ttt.rows);
        // cv.imshow("templateCanvasInput_tt", ttt);

        // return;

        // templ 22x24 w < h
        let max_size = null;
        let W = src_bag.cols;
        let H = src_bag.rows;

        let w = templ.cols;
        let h = templ.rows;
        if (W < w) {
            let t = W;
            W = w;
            w = t;

            t = H;
            H = t;
            h = t;
        }
        const isFlat = (W / H) > (w / h);
        let scale = 1;
        let step = null;
        if (isFlat) {
            // 按高度适配，最高不会超过H
            scale = (H / h);
            max_size = {
                w: w * scale,
                h: H,
            };
            step = {
                h: (max_size.h - h) * 0.01,
            };
        } else {
            // 按宽度适配，最宽不会超过W
            scale = (W / w);
            max_size = {
                w: W,
                h: h * scale,
            };
            step = {
                w: (max_size.w - w) * 0.01,
            };
        };

        let ttt = src_bag.clone();

        /**
         * @done  window.$mocvf 的注释
         * @example     
         * var $mocvf = ...;
         * Object.defineProperty(window, "$mocvf", $mocvf);
         * */
        let res = window.$mocvf.scaleSearch(src_b_w, templ_b_w, dst, mask, {
            similarity: 60,
            range: [{
                w: max_size.w,
                h: max_size.h
            }, {
                w: w,
                h: h
            }],
            step,
            drawSrc: ttt
        });

        // -----绘制矩形选框
        // cv.imshow('rune_num_marked_Canvas', drawSrc);

        cv.imshow('ttt', ttt);


        console.log(res);
        let color = new cv.Scalar(255, 0, 0, 255);
        cv.rectangle(src_bag_c, res.match_rect_data_loc, res.match_rect_data_point, color, 2, cv.LINE_8, 0);
        cv.imshow('rune_rect_marked_Canvas', src_bag_c);


        let matchLoc = res.match_rect_data_loc;
        let point = res.match_rect_data_point;
        let match_rect = window.$mocvf.calcRect(matchLoc, point);

        // ------存储缩放模板匹配结果
        runes_matched_data.rect = match_rect;
        // runes_matched_data.templ = templ;// 模板数据
        runes_matched_data.area_o = src_bag.roi(match_rect);// 模板匹配成功区域(拿原图，非二值化，用克隆前的图片)
        runes_matched_data.templ = templ_b_w;// 模板数据(二值化)
        runes_matched_data.area = src_b_w.roi(match_rect);// 模板匹配成功区域(二值化)
        runes_matched_data.checkState = CHECK_STATE.INIT;//设置初始校验状态
        runes_matched_data.matchLoc = matchLoc;
        runes_matched_data.point = point;


        templ.delete();
        src_b_w.delete();
        // templ_b_w.delete(); // MatchedData.templ引用，最下面清
        dst.delete();
        mask.delete();
    }
    // console.log("runes_matched_data", runes_matched_data);

    // ----- 轮廓匹配数字
    {
        let color1 = new cv.Scalar(0, 255, 255, 255); // 轮廓颜色（天蓝色）
        let color2 = new cv.Scalar(3, 255, 32, 255); // 外接矩形颜色（数字框颜色）

        // ----模板图片导入与预处理
        let ele_templ_list = document.getElementById("templ_list");
        let content = "";

        for (let n = 0; n < NUMS_DATA.length; n++) {

            if (n < DEF_NUMS_AREA[0]) {
                continue;
            }
            if (n > DEF_NUMS_AREA[1]) {
                break;
            }
            if (n == 0 || n == 8) {
                continue; // 0和8暂时不考虑
            }
            let num = NUMS_DATA[n];
            let img_num = num_imgs.get(num.name);
            if (img_num === undefined) {
                console.warn(`num ${num.name} 图片加载失败`);
                continue;
            }
            // console.log(img_num.width, img_num.height);
            content +=
                `<canvas id="${'num_' + num.name}" width="${img_num.width}" height="${img_num.height}"></canvas>`;
        }
        ele_templ_list.innerHTML = content;// 创建canvas


        for (let n = 0; n < NUMS_DATA.length; n++) {

            if (n < DEF_NUMS_AREA[0]) {
                continue;
            }
            if (n > DEF_NUMS_AREA[1]) {
                break;
            }
            if (n == 0 || n == 8) {
                continue; // 0和8暂时不考虑
            }
            let num = NUMS_DATA[n];
            let img_num = num_imgs.get(num.name);
            if (img_num === undefined) {
                console.warn(`num ${num.name} 图片加载失败`);
                continue;
            }

            let canvas_id = 'num_' + num.name;
            let canvas = document.getElementById(canvas_id);
            let ctx = canvas.getContext('2d', {
                willReadFrequently: true,
            });
            ctx.drawImage(img_num, 0, 0);// 导入图片


            let templ = cv.imread(canvas_id); // num_x模板
            cv.cvtColor(templ, templ, cv.COLOR_RGBA2GRAY, 0); //转化为单通道灰度图
            cv.threshold(templ, templ, 220, 255, cv.THRESH_BINARY); // 3 thresh 阈值，4：maxval最大值

            // 模板轮廓绘制
            let contours = new cv.MatVector();
            let hierarchy = new cv.Mat();
            let dst = cv.Mat.zeros(templ.cols, templ.rows, cv.CV_8UC3);
            cv.findContours(templ, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);

            let contours_order = 0;
            /**
             * @todo 数字0和8轮廓要特殊处理，这里不考虑了（游戏中背包符石数量很难达到8（更别说10了），而且没有0这种情况），如果真想处理可以使用cv.minMaxLoc ，遍历所有轮廓，取出四个rect的顶点。
             */
            cv.drawContours(dst, contours, contours_order, color1, 1, cv.LINE_8, hierarchy,
                100); // 轮廓绘制
            // 外接矩形绘制
            let cnt = contours.get(contours_order);
            let rect_t = cv.boundingRect(cnt);

            // cv.imshow(canvas_id, templ);
            // console.log(rect);
            dst = dst.roi(rect_t);
            // cv.resize(dst, dst, new cv.Size(rect_t.width, rect_t.height)); // 缩放模板图片

            cv.imshow(canvas_id, dst);
            contours.delete();
            hierarchy.delete();
            cnt.delete();
            templ.delete();
            dst.delete();
        }

        // ----识别数字
        {
            let rune = runes_matched_data;
            let src_num = rune.area_o; // 符石所在区域，待查询
            let src_num_c = src_num.clone(); //

            let src_num_c1 = src_num.clone(); // 另外clone一份 用作表格数据展示
            let src_num_c2 = src_num.clone(); // 另外clone一份 用作表格数据展示
            // cv.imshow("rune_rect_marked_Canvas", src_num_c1);
            cv.imshow("canvas1", src_num_c1);

            let arr_l = [240, 0, 0, 255];
            let arr_h = [255, 255, 255, 255];

            // 二值化待搜索图片
            let src_b_w = new cv.Mat();// 用于保存二值化图片
            bwImage(src_num_c, arr_l, arr_h, src_b_w);

            // 转化数据 cv.CV_8U->cv.CV_8UC4
            cv.imshow("numCanvas", src_b_w);
            src_b_w = cv.imread("numCanvas"); // 二值化图
            cv.imshow("numCanvas", src_num_c); // 最后显示的还是正常的图片

            // cv.imshow(result_table_data[m].canvas2_t, src_b_w);

            /** 数量已知在右下角 */
            // console.log(src_b_w.cols, src_b_w.rows);
            let numRect = new cv.Rect(src_b_w.cols / 2, src_b_w.rows / 2, src_b_w.cols / 2, src_b_w.rows / 2);
            /** @todo 除了裁剪图片还可以试试canvas rect填充右上角的等级文字 */
            let num_src_b_w = src_b_w.roi(numRect).clone();
            // cv.imshow("canvas2_t", num_src_b_w);


            let contours = new cv.MatVector();
            let hierarchy = new cv.Mat();
            let dst = cv.Mat.zeros(num_src_b_w.cols, num_src_b_w.rows, cv.CV_8UC3);
            // let dst = num_src_b_w.clone();
            // 将三通道图像（彩色图）转化为单通道图（灰度图）；第三个参数flag 转换模式 cv2.COLOR_RGB2GRAY 彩色转灰度 cv2.COLOR_GRAY2RGB 单通道转多通道
            cv.cvtColor(num_src_b_w, num_src_b_w, cv.COLOR_RGBA2GRAY, 0);
            cv.threshold(num_src_b_w, num_src_b_w, 220, 255, cv.THRESH_BINARY);

            let step_ncm_error = false;// 标识这步数字轮廓匹配是否异常
            try {
                // if (m % 2 == 0)
                //     throw new Error("模拟轮廓匹配待识别数字错误");
                // cv.imshow(result_table_data[m].canvas2_t, num_src_b_w);
                cv.findContours(num_src_b_w, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);

                /** 
                 * @todo 这个写死可能会有问题，比如符石匹配异常，游戏截图时有系统提示/浮屏应用遮挡；造成图片复杂，轮廓数量差异，以至于轮廓顺序差异 
                 * @todo #7367 截图分辨率不一致时这边就出问题了
                */
                let contours_order = 0;// screenshot1
                // let contours_order = 0;// screenshot2 
                // 外接矩形绘制
                // for (let i = 0; i < contours.size(); ++i) {
                // cv.drawContours(src_num_c, contours, contours_order, color1, 1, cv.LINE_8, hierarchy, 100); // 轮廓绘制
                cv.drawContours(dst, contours, contours_order, color1, 1, cv.LINE_8, hierarchy, 100); // 轮廓绘制
                // cv.imshow(result_table_data[m].canvas2_t, dst);
                let cnt = contours.get(contours_order);

                // cv.drawContours(dst, contours, i, color1, 1, cv.LINE_8, hierarchy, 100); // 轮廓绘制
                // let cnt = contours.get(i);
                let rect = cv.boundingRect(cnt);
                // let point1 = new cv.Point(rect.x, rect.y);
                // let point2 = new cv.Point(rect.x + rect.width, rect.y + rect.height);
                let point1 = new cv.Point(rect.x + src_num_c2.cols / 2, rect.y + src_num_c2.rows / 2);
                let point2 = new cv.Point(rect.x + rect.width + src_num_c2.cols / 2, rect.y + rect.height + src_num_c2.rows / 2);
                cv.rectangle(src_num_c2, point1, point2, color2, 1, cv.LINE_AA, 0);
                /**@done 原图上矩形选框颜色怪怪的, cv.Scalar是四个参数，就填三个肯定有问题呀 */
                cv.imshow("canvas2", src_num_c2);// 表格展示数量匹配结果

                let dst_c = dst.clone();
                cv.rectangle(dst, new cv.Point(rect.x, rect.y), new cv.Point(rect.x + rect.width, rect.y + rect.height), color2, 1, cv.LINE_AA, 0);
                // cv.imshow(result_table_data[m].canvas2_t, dst);
                dst = dst_c.roi(rect);
                cv.imshow("canvas2_t", dst);
                cnt.delete();

                // cv.imshow(result_table_data[m].canvas2_t, dst);
                // }
                // cv.imshow(result_table_data[m].canvas2_t, dst);
            } catch (error) {
                step_ncm_error = true;
                console.error("轮廓匹配待识别数字异常", error);
            }
            let similarity = 0;
            let result_num = -1;
            // ----用mmCompare识别数字
            try {
                if (step_ncm_error) {
                    throw new Error("模拟数字识别错误");
                }
                for (let n = 0; n < NUMS_DATA.length; n++) {
                    if (n < DEF_NUMS_AREA[0]) {
                        continue;
                    }
                    if (n > DEF_NUMS_AREA[1]) {
                        break;
                    }
                    if (n == 0 || n == 8) {
                        continue; // 0和8暂时不考虑
                    }
                    let num = NUMS_DATA[n];
                    let img_num = num_imgs.get(num.name);
                    if (img_num === undefined) {
                        console.warn(`num ${num.name} 图片加载失败`);
                        continue;
                    }
                    let canvas_id = 'num_' + num.name;
                    let templ = cv.imread(canvas_id);
                    let dst = cv.imread("canvas2_t");
                    let res = window.$mocvf.mmCompare(dst, templ);
                    // let res = mmCompare(templ, dst);
                    if (res > similarity) {
                        similarity = res;
                        result_num = n;
                    }

                }
                console.log(`数字${result_num}的相似度(${similarity})最高`);
            } catch (error) {
                console.error("用mmCompare识别数字异常", error);
            }
            // cv.imshow(result_table_data[m].canvas2, src_num_c2);// 表格展示数量匹配结果
            // result_table_data[m].num = result_num;// 表格展示匹配数量值

        }
    }

    console.timeEnd("识别时间");
}

/**
 * @function 
 * @description select onchange事件触发函数，修改时设置图片
 */
function selectRune() {
    let rune_name = document.getElementById("select_rune").value;
    // console.log(rune_name);
    let res = RUNES_DATA.find((val) => val.name == rune_name);
    if (!res) {
        console.warn("查询符石异常");
        return;
    }
    // console.log(res);
    let rune = res;
    let rune_src = rune.src;
    document.getElementById("rune_img").src = "../../imgs" + rune_src;
}

/**
 * @function 
 * @description 加载截图并显示(konva)
 * @param { OP_STEP }  currStep
 */
function showScImg() {
    let item = localStorage.getItem("sc_img_data");
    if (!item) {
        console.warn("获取截图数据异常！");
        return;
    }
    // { img: imgData// 图片, size: imgSize // 适配过的宽高}
    imgData = JSON.parse(item);
    // console.log(imgData);

    let sc_w = imgData.size.width;
    let sc_h = imgData.size.height;
    // console.log("img:", sc_w, sc_h);

    let img = new Image();
    img.onload = () => {
        // 用户看到的图片
        let img1 = new Konva.Image({
            image: img,
            width: sc_w,
            height: sc_h,
            x: 0,
            y: 0,
            name: 'image', // 相当于 class 可以以空格分隔，多个name
            id: image1_id, // 相当于id
            draggable: false,
            // opacity: 0.1
        });
        layer1.add(img1);

        // 真实截图实际大小，裁剪用
        let img1_o = new Konva.Image({
            image: img,
            width: imgData.size.origin_width,
            height: imgData.size.origin_height,
            x: 0,
            y: 0,
            name: 'img1_o',
            id: image1_o_id,
            draggable: false,
        });
        img1_o.opacity(0);
        layer1.add(img1_o);


        // layer2.add(img);// 直接添加相当于换父节点
        let outer1 = img1.clone().opacity(0);
        outer1.id(outer1_id);
        outer1.width(vw);
        outer1.height(vh);
        layer1.add(outer1); // #3788 防止事件直接捕获到img1
        outer1.moveToBottom();// touchMove超区判断用
        outer1.moveUp();
        img1_o.moveToBottom();// 截图用

        let outer2 = img1.clone().opacity(0);
        outer2.id(outer2_id);
        outer2.width(vw);
        outer2.height(vh);
        layer2.add(outer2); // #3788 防止事件直接捕获到img2

        layer2.add(text2);

        stage.add(layer1);
        stage.add(layer2);
        layer2.hide();

        IS_DEBUG && debugStep(DEBUG_CURR_STEP);
    };
    img.src = imgData.img;




}


function writeMessage(message) {
    text.text(message);
}

function writeMessage2(message) {
    text2.text(message);
}


function touchStartHandler1(evt) {

    if (evt.target.id() != image1_id) {
        console.log("未在图片内，退出");
        return;
    }

    var touchPos = stage.getPointerPosition();
    var x = touchPos.x;
    var y = touchPos.y;

    drawing_rect1 = true;
    start_point1 = {
        x,
        y
    };
    writeMessage('Touchstart layer1');
}

function touchMoveHandler1(evt) {
    if (!drawing_rect1) {
        console.log("未开始绘制，退出");
        return;
    }

    if (evt.target.id() == outer1_id) { // #3788
        console.log("超出图片外");
        drawing_rect1 = false;
        start_point1 = null;

        // console.log("rect1", rect1);
        if (rect1) {
            o_btn_ok.style.display = "block";
            o_btn_restart.style.display = "block";

            layer1.off('touchmove');
            layer1.off('touchstart');
            layer1.off('touchend');

            console.log("立即结束当前绘制");
        }
        return;
    }
    var touchPos = stage.getPointerPosition();
    // var x = touchPos.x - 190;
    // var y = touchPos.y - 40;
    var x = touchPos.x;
    var y = touchPos.y;
    writeMessage('x: ' + x + ', y: ' + y);
    changeRect1(x, y);
}

function changeRect1(x, y) {
    if (drawing_rect1) {

        let offset_x = Math.abs(start_point1.x - x);
        let offset_y = Math.abs(start_point1.y - y);
        if (offset_x > draw_pint_threshold || offset_y > draw_pint_threshold) {
            if (!rect1) {
                // console.log("初次创建rect");
                rect1 = new Konva.Rect({
                    id: "rect1",
                    x: start_point1.x,
                    y: start_point1.y,
                    width: offset_x,
                    height: offset_y,
                    fill: '',
                    stroke: rect_stroke_color1,
                    strokeWidth: 2,
                });
                // add the shape to the layer1
                layer1.add(rect1);
                // rect1.draw();
            } else {
                rect1.size({
                    width: offset_x,
                    height: offset_y,
                })
            }
        }


    }
}

function clearAndOff1() {
    drawing_rect1 = false;
    start_point1 = null;

    layer1.off('touchmove');
    layer1.off('touchstart');
    layer1.off('touchend');
    rect1 && rect1.destroy();
    rect1 = null;
}


function touchEndHandler1(evt) {
    writeMessage('Touchend');
    if (!drawing_rect1) {
        console.log("未开始绘制，退出");
        return;
    }

    drawing_rect1 = false;
    start_point1 = null;

    if (!rect1) {
        console.log("没有touchmove或者移动小于draw_pint_threshold");
        return;
    }

    layer1.off('touchmove');
    layer1.off('touchstart');
    layer1.off('touchend');


    o_btn_ok.style.display = "block";
    o_btn_restart.style.display = "block";

}

function touchStartHandler2(evt) {
    // console.log("touchStart", evt.target.id());
    // console.log("touchStart");
    if (evt.target.id() != image2_id) {
        console.log("未在图片内，退出");
        return;
    }
    // evt.currentTarget (不会用 ，拿不到id), evt.target
    // var target = evt.target; // 事件委托
    // console.log("start,target:", target);
    // if(){

    // }
    var touchPos = stage.getPointerPosition();

    var x = touchPos.x;
    var y = touchPos.y;

    drawing_rect2 = true;
    start_point2 = {
        x,
        y
    };
    writeMessage2('Touchstart bag_area_img');
}

function touchMoveHandler2(evt) {
    // console.log("touchMove",evt.currentTarget && evt.currentTarget.name());
    // console.log("touchMove");
    // var target = evt.target; // 事件委托
    // console.log("move，target:", target); // 事件委托
    if (!drawing_rect2) {
        console.log("未开始绘制，退出");
        return;
    }
    // console.log("target:id", evt.target.id());
    // console.log("currentTarget:id", evt.currentTarget.id());
    // console.log("currentTarget", evt.currentTarget);
    // if (evt.target.id() != image2_id) {// #3788 如果碰到正在绘制的rect2也会暂停
    if (evt.target.id() == outer2_id) { // #3788
        console.log("超出图片外");
        drawing_rect2 = false;
        start_point2 = null;

        // console.log("rect2", rect2);
        /**@done 需要小于阈值的情况 */
        if (rect2) {
            getRealRect2();

            o_btn_ok.style.display = "block";
            o_btn_restart.style.display = "block";

            layer2.off('touchmove');
            layer2.off('touchstart');
            layer2.off('touchend');

            console.log("立即结束当前绘制");
        }

        return;
    }
    var touchPos = stage.getPointerPosition();

    var x = touchPos.x;
    var y = touchPos.y;
    writeMessage2('x: ' + x + ', y: ' + y);
    changeRect2(x, y);
}

function changeRect2(x, y) {
    if (drawing_rect2) {

        let offset_x = Math.abs(start_point2.x - x);
        let offset_y = Math.abs(start_point2.y - y);
        if (offset_x > draw_pint_threshold || offset_y > draw_pint_threshold) {
            if (!rect2) {
                // console.log("初次创建rect");
                rect2 = new Konva.Rect({
                    id: "rect2",
                    x: start_point2.x,
                    y: start_point2.y,
                    width: offset_x,
                    height: offset_y,
                    fill: '',
                    stroke: rect_stroke_color2,
                    strokeWidth: 2,
                });
                // add the shape to the layer1
                layer2.add(rect2);

                // bag_area_img.add(rect2);// node，没有add方法
            } else {
                rect2.size({
                    width: offset_x,
                    height: offset_y,
                });
            }
        }


    }
}

function clearAndOff2() {
    drawing_rect2 = false;
    start_point2 = null;

    layer2.off('touchmove');
    layer2.off('touchstart');
    layer2.off('touchend');
    rect2 && rect2.destroy();
    rect2 = null;
}


function touchEndHandler2(evt) {
    // var target = evt.target; // 事件委托
    // console.log("touchEnd",evt.currentTarget);
    console.log("touchEnd");
    // console.log("end,target:", target);
    // writeMessage2('Touchend bag_area_img');
    if (!drawing_rect2) {
        console.log("未开始绘制，退出");
        return;
    }
    drawing_rect2 = false;
    start_point2 = null;

    if (!rect2) {
        console.log("没有touchmove或者移动小于draw_pint_threshold");
        return;
    }

    // console.log("rect2", rect2);
    getRealRect2();


    o_btn_ok.style.display = "block";
    o_btn_restart.style.display = "block";

    layer2.off('touchmove');
    layer2.off('touchstart');
    layer2.off('touchend');
}

/**
 * @function 
 * @description 获取选中符石相对选中背包区域的真实Rect(本身它是可拖拽、缩放的)
 */
function getRealRect2() {
    /*** @todo 到这步发现个问题，一开始就知道node没有add方法，我应该将image2和rect放在同一个group里面操作，这样取相对坐标方便点**/
    // 但写到现在再改的话，改动有点多，就直接计算吧

    // console.log("rect2", rect2);
    // let tr = layer2.findOne('#tr_bag');

    let bag_area_img = layer2.findOne("#" + image2_id);

    // let pos_img = bag_area_img.position();
    let pos_img = bag_area_img.getAbsolutePosition();
    console.log(pos_img.x, pos_img.y);

    // rect2 position relative to bag_area_img
    let x = rect2.getAttr('x') - pos_img.x;
    let y = rect2.getAttr('y') - pos_img.y;
    // console.log(x, y, bagRect);
    // console.log(bag_area_img.scale());// 缩放比例
    let ratio_scale = bag_area_img.scale().x; // 取x,y值都行，因为是等比缩放
    console.log(x, y, ratio_scale);

    /**@todo 浮点数计算误差*/
    runeRect = {
        x: Math.ceil(x / ratio_scale),
        y: Math.ceil(y / ratio_scale),
        width: Math.floor(rect2.width() / ratio_scale),
        height: Math.floor(rect2.height() / ratio_scale),
    };

    // console.log(runeRect);
    // showPaintedImage2();
}

/**
 * @function 
 * @description 调试用，用于展示绘制选区在原来图片上是否正常
 */
function showPaintedImage2() {
    let group = new Konva.Group({
        x: 0,
        y: 0,
        width: image2_c.width(),
        height: image2_c.height(),
        draggable: true
    });

    layer2.add(group);
    group.add(image2_c);
    image2_c.draggable(false);

    image2_c.position({
        x: 0,
        y: 0
    });

    let rect = new Konva.Rect({
        id: "image2_c_rect",
        x: runeRect.x,
        y: runeRect.y,
        width: runeRect.width,
        height: runeRect.height,
        fill: '',
        stroke: rect_stroke_color2,
        strokeWidth: 2,
    });
    group.add(rect);
}


let o_btn_start1 = document.getElementById("btn_start1");
let o_btn_start2 = document.getElementById("btn_start2");
let o_btn_prev_step = document.getElementById("btn_prev_step");
let o_btn_restart = document.getElementById("btn_restart");
let o_btn_quit = document.getElementById("btn_quit");
let o_btn_ok = document.getElementById("btn_ok");
let o_btn_back = document.getElementById("btn_back");
let o_btn_finish = document.getElementById("btn_finish");
let o_btn_match = document.getElementById("btn_match");

// add button event bindings
o_btn_start1.addEventListener(
    'click',
    function (event) {
        // event.stopPropagation();
        // event.preventDefault();
        console.log("btn_start1 click");


        o_btn_start1.style.display = "none";

        o_btn_quit.style.display = "block";

        layer1.on('touchmove', touchMoveHandler1);
        layer1.on('touchstart', touchStartHandler1);
        layer1.on('touchend', touchEndHandler1);

        return false;

    },
    false
);



o_btn_start2.addEventListener(
    'click',
    function (event) {
        // event.stopPropagation();
        // event.preventDefault();
        console.log("btn_start2 click");

        o_btn_start2.style.display = "none";
        o_btn_prev_step.style.display = "none";

        // o_btn_restart.style.display = "block";
        o_btn_quit.style.display = "block";
        // o_btn_ok.style.display = "block";

        // 绘制过程中取消缩放，拖拽
        let tr = layer2.findOne('#tr_bag');
        tr.resizeEnabled(false);

        let bag_area_img = layer2.findOne("#" + image2_id);
        bag_area_img.draggable(false);

        clearAndOff2();

        layer2.on('touchmove', touchMoveHandler2);
        layer2.on('touchstart', touchStartHandler2);
        layer2.on('touchend', touchEndHandler2); // on只有两个参数 没法取消事件捕获
        // console.log("layer2:",layer2.width(), layer2.height(),layer2.position());


        return false;

    },
    false
);

o_btn_prev_step.addEventListener(
    'click',
    function (event) {
        // event.stopPropagation();
        // event.preventDefault();
        console.log("btn_prev_step click");
        curr_step = OP_STEP.A;
        o_btn_prev_step.style.display = "none";
        o_btn_start2.style.display = "none";
        layer1.show();
        layer2.destroyChildren();
        layer2.hide();

        o_btn_restart.style.display = "block";
        o_btn_quit.style.display = "block";
        o_btn_ok.style.display = "block";



        return false;

    },
    false
);


o_btn_restart.addEventListener(
    'click',
    function (event) {
        // event.stopPropagation();
        // event.preventDefault();
        console.log("o_btn_restart click", curr_step);
        o_btn_ok.style.display = "none";
        o_btn_restart.style.display = "none";

        if (curr_step == OP_STEP.A) {
            clearAndOff1();
            layer1.on('touchmove', touchMoveHandler1);
            layer1.on('touchstart', touchStartHandler1);
            layer1.on('touchend', touchEndHandler1);
        } else if (curr_step == OP_STEP.B) {
            clearAndOff2();
            layer2.on('touchmove', touchMoveHandler2);
            layer2.on('touchstart', touchStartHandler2);
            layer2.on('touchend', touchEndHandler2);
        }


        return false;

    },
    false
);

o_btn_quit.addEventListener(
    'click',
    function (event) {
        // event.stopPropagation();
        // event.preventDefault();
        console.log("o_btn_quit click");

        o_btn_restart.style.display = "none";
        o_btn_quit.style.display = "none";
        o_btn_ok.style.display = "none";

        if (curr_step == OP_STEP.A) {
            o_btn_start1.style.display = "block";

            clearAndOff1();

        } else if (curr_step == OP_STEP.B) {
            o_btn_start2.style.display = "block";
            o_btn_prev_step.style.display = "block";

            clearAndOff2();

            // 图片可移动、拖拽
            let tr = layer2.findOne('#tr_bag');
            tr.resizeEnabled(true);

            let bag_area_img = layer2.findOne("#" + image2_id);
            bag_area_img.draggable(true);
        }

        return false;

    },
    false
);

o_btn_ok.addEventListener(
    'click',
    function (event) {
        // event.stopPropagation();
        // event.preventDefault();
        // console.log("o_btn_ok click", rect1);
        // console.log("o_btn_ok click");

        if (curr_step == OP_STEP.A) {
            curr_step = OP_STEP.B;
            o_btn_restart.style.display = "none";
            o_btn_quit.style.display = "none";
            o_btn_ok.style.display = "none";

            o_btn_prev_step.style.display = "block";
            o_btn_start1.style.display = "none";
            o_btn_start2.style.display = "block";



            // 拿到选区（原图比例）
            console.log(rect1.getAttr('x'), imgData.scale);
            let x = Math.floor(rect1.getAttr('x') / imgData.scale);
            let y = Math.floor(rect1.getAttr('y') / imgData.scale);
            let width = Math.ceil(rect1.getAttr('width') / imgData.scale);
            let height = Math.ceil(rect1.getAttr('height') / imgData.scale);

            bagRect = {
                x,
                y,
                width,
                height
            };
            console.log("bagRect:", bagRect);

            // 根据选区生成图片并显示到layer2

            // let img1 = layer1.find('#img1');// [ve]
            // let img1 = layer1.findOne('#img1');
            // let img1 = layer1.findOne("#" + image1_id);
            let img1_o = layer1.findOne("#" + image1_o_id);
            let img2 = img1_o.clone().opacity(1);
            img2.toImage({
                callback(img) {
                    // console.log("xxx", img1);// htmlEl
                    let sc_w = img.width;
                    let sc_h = img.height;

                    let image = new Konva.Image({
                        id: image2_id,
                        image: img,
                        width: sc_w,
                        height: sc_h,
                        x: vw * 0.1,
                        y: vh * 0.2,
                        name: 'image2',
                        draggable: true,
                        // opacity: 0.1
                    });
                    layer2.add(image);
                    image2_c = image.clone();// 缩放移动前的，裁剪用
                    image2_c.id("image2_c");
                    // layer.draw();

                    let tr = new Konva.Transformer({
                        node: image,
                        id: "tr_bag",
                        keepRatio: true,
                        /***@todo 是不是有集成好的裁剪功能 */
                        // flipEnabled 好像可以裁剪
                        rotateEnabled: false, // 禁止旋转
                        enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right']
                    });
                    // console.log(tr);
                    layer2.add(tr);
                    // layer.draw();
                },
                mimeType: "image/jpeg",
                x: bagRect.x,
                y: bagRect.y,
                width: bagRect.width,
                height: bagRect.height,
                quality: 0.5, // 0-1
                pixelRatio: 1, // 
                imageSmoothingEnabled: false,
            });


            layer1.hide();
            layer2.show();


        } else if (curr_step == OP_STEP.B) {
            curr_step = OP_STEP.C;
            o_btn_restart.style.display = "none";
            o_btn_quit.style.display = "none";
            o_btn_ok.style.display = "none";

            o_btn_back.style.display = "block";
            // o_btn_finish.style.display = "block";

            // 根据选区生成图片
            console.log("runeRect:", runeRect);
            // return;

            // layer2.add(image2_c);
            // layer2.draw();

            let image2_ = image2_c.clone();
            image2_.position({
                x: 0,
                y: 0
            });


            image2_.toImage({
                callback(img) {
                    // let image = new Konva.Image({
                    //     id: "choose-rune-img",
                    //     image: img,
                    //     width: img.width,
                    //     height: img.height,
                    //     x: 0,
                    //     y: 0,
                    //     name: 'choose-rune-img',
                    //     draggable: false,
                    //     // opacity: 0.1
                    // });
                    // layer2.add(image);

                    // console.log(img);
                    document.getElementById("check_menu").style.display = "block";
                    document.getElementById("td-rune_rect").appendChild(img);

                    let sc_w = img.width;
                    let sc_h = img.height;
                    image3 = new Konva.Image({
                        id: image2_id,
                        image: img,
                        x: 0,
                        y: 0,
                        width: sc_w,
                        height: sc_h,
                        name: 'image3',
                        draggable: true, // 拖拽是图片节点的功能
                        // opacity: 0.1
                    });

                },
                mimeType: "image/jpeg",
                x: runeRect.x,
                y: runeRect.y,
                width: runeRect.width,
                height: runeRect.height,
                quality: 1, // 0-1
                pixelRatio: 1, // 
                imageSmoothingEnabled: true,
            });


            // layer2.hide();
        }





        return false;// 防止点击事件冒泡

    },
    false
);

o_btn_back.addEventListener(
    'click',
    function (event) {
        // event.stopPropagation();
        // event.preventDefault();
        console.log("o_btn_back click");

        curr_step = OP_STEP.B;

        o_btn_restart.style.display = "block";
        o_btn_quit.style.display = "block";
        o_btn_ok.style.display = "block";

        o_btn_back.style.display = "none";

        document.getElementById("check_menu").style.display = "none";
        document.getElementById("td-rune_rect").innerHTML = "";


        return false;

    },
    false
);

o_btn_match.addEventListener(
    'click',
    function (event) {
        // event.stopPropagation();
        // event.preventDefault();
        console.log("o_btn_match click");
        defRune();
        return false;

    },
    false
);

/***
 * @function
 * @description 加载图片资源
 * @param { string } url 图片地址
 * 
 * @returns { Promise } 
 * 
 * 
*/
function loadImg(url) {
    return new Promise((resolve, reject) => {
        let img = new Image();
        // img.crossOrigin = 'anonymous';
        // 跨域 直接用live server插件运行
        img.src = url;
        img.onload = () => {
            resolve(img);
        };
        img.onerror = reject;
    });
}